import threading
import yaml
import json
import datetime
import pathlib
import time
import shutil
import subprocess
import os
import sys
import re
import requests
import psutil
import ctypes
import typing
import configparser
import zipfile
import sqlite3
import random
import logging
import cv2
import base64
import io
import PIL.Image

from selenium.webdriver.edge.webdriver import WebDriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import *




class Custom_WebDriver:
    """单例类，用于管理 Edge 浏览器的 WebDriver 实例。"""

    _instance = None

    def __new__(cls, existing_driver=None) -> 'Custom_WebDriver':
        """确保 Custom_WebDriver 只有一个实例。

        Args:
            existing_driver (WebDriver, optional): 已有的 WebDriver 实例。默认为 None。

        Returns:
            Custom_WebDriver: 类的单例实例。
        """
        if cls._instance is None:
            cls._instance = super(Custom_WebDriver, cls).__new__(cls)
            if existing_driver:
                cls._instance.driver = existing_driver
            else:
                cls._instance.options = Options()
                cls._instance.service = Service()
                cls._instance.options.add_experimental_option("debuggerAddress", "127.0.0.1:9652")
                cls._instance.driver = WebDriver(options=cls._instance.options)
            cls._instance.actions = ActionChains(cls._instance.driver)
            cls._instance.wait = WebDriverWait(driver=cls._instance.driver, timeout=10)
        return cls._instance

    def __init__(self, existing_driver=None) -> None:
        """初始化浏览器驱动类，设置选项和服务，并启动 WebDriver 实例。

        Args:
            existing_driver (WebDriver, optional): 已有的 WebDriver 实例。默认为 None。
        """
        if existing_driver:
            self.driver = existing_driver
        else:
            self.options = Options()
            self.service = Service()
            self.options.add_experimental_option("debuggerAddress", "127.0.0.1:9652")
            self.driver = WebDriver(options=self.options)

    def Goto_CurrentActivateUrl(self) -> 'Custom_WebDriver':
        """切换 Selenium 的 Handle 操作句柄到当前激活的页面

        Returns:
            Custom_WebDriver: 类对象本身
        """
        debug_json_data = requests.get(
            url="http://127.0.0.1:9652/json", timeout=5).json()

        # 判断获取回来的数据是否正确
        if not isinstance(debug_json_data, list):
            print("接口返回数据格式不正确（非列表或为空）")
            return None

        # 过来掉不需要的 page 页面
        filter_json_data = list(
                                    filter(lambda item: (
                                        item['type'] == "page" and
                                        not item['title'].lower().startswith("devtools") and
                                        not item['title'].lower().startswith("offscreen") and
                                        not item['title'].lower().startswith("新标签页") and
                                        not item['url'].lower().startswith("edge://newtab/") and
                                        not item['url'].lower().startswith("chrome-extension") and
                                        not item['url'].lower().startswith("chrome-devtools")
                                    ), debug_json_data)
                                )
        current_activate_handle = filter_json_data[0]['id']
        self.driver.switch_to.window(window_name=filter_json_data[0]['id'])
        return self

    # 上方为基础函数,不需要进行修改.

    def Findelement(self, xpathvalue: str) -> 'Custom_WebDriver':
        """通过 XPath 查找单个元素，并将结果存储在实例变量 _element 中。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        try:
            self._element = self.driver.find_element(by=By.XPATH, value=xpathvalue)
            return self
        except NoSuchElementException:
            print(f"提示 : 无法找到目标元素!")
            return self
    
    def Implicitly_findelement(self, xpathvalue: str, timeout: float = 10) -> 'Custom_WebDriver':
        """通过 XPath 查找单个元素，如果在指定时间内未找到则打印提示信息。

        Args:
            xpathvalue (str): 要查找的元素的 XPath 路径。
            timeout (float): 查找元素的最大等待时间（秒）。

        Returns:
            Custom_WebDriver: 返回当前实例对象。
        """
        start_time = time.time()
        while time.time() - start_time < timeout:
            try:
                self._element = self.driver.find_element(by=By.XPATH, value=xpathvalue)
                return self
            except NoSuchElementException:
                time.sleep(0.5)  # 每隔0.5秒重试一次
        print(f"提示 : 无法找到目标元素!")
        return self
    
    

    def by_near_text_find_element(self, text: str, element_tag: str = "input", element_type: str = "file", 
                                text_tags: list = None, max_depth: int = 5) -> 'Custom_WebDriver':
        """
        根据附近文本查找元素
        
        Args:
            text: 附近的文本内容
            element_tag: 要查找的元素标签
            element_type: 要查找的元素类型（如file、text等）
            text_tags: 文本所在的标签类型，默认['span', 'label', 'div']
            max_depth: 最大搜索深度
        """
        if text_tags is None:
            text_tags = ['span', 'label', 'div']
        
        # 构建文本元素查找XPath
        tag_selector = " or ".join([f"self::{tag}" for tag in text_tags])
        text_xpath = f"//*[({tag_selector}) and contains(text(), '{text}')]"
        
        try:
            # 查找文本元素
            text_elements = self.driver.find_elements(By.XPATH, text_xpath)
            
            for text_element in text_elements:
                # 在附近查找目标元素
                target_element = self._find_element_near_text_element(
                    text_element, element_tag, element_type, max_depth)
                if target_element:
                    self._element = target_element
                    return self
                    
        except Exception as e:
            print(f"根据附近文本查找元素出错: {e}")
        
        print(f"提示: 未找到文本 '{text}' 附近的 {element_tag}[type='{element_type}'] 元素")
        return self

    def _find_element_near_text_element(self, text_element: WebElement, element_tag: str, 
                                      element_type: str, max_depth: int) -> WebElement:
        """在文本元素附近查找目标元素"""
        current_element = text_element
        
        for depth in range(max_depth):
            try:
                # 尝试多种相对位置查找
                patterns = [
                    f"./following-sibling::*//{element_tag}[@type='{element_type}']",
                    f"./preceding-sibling::*//{element_tag}[@type='{element_type}']", 
                    f"./parent::*//{element_tag}[@type='{element_type}']",
                    f"./ancestor::*[position()={depth+1}]//{element_tag}[@type='{element_type}']"
                ]
                
                for pattern in patterns:
                    try:
                        element = current_element.find_element(By.XPATH, pattern)
                        if element:
                            return element
                    except NoSuchElementException:
                        continue
                        
            except Exception:
                pass
            
            # 向上移动一层
            try:
                current_element = current_element.find_element(By.XPATH, "./parent::*")
            except NoSuchElementException:
                break
        
        return None

    def with_condition_find_element(self, xpath: str, condition_type: str = None, 
                                  expected_value: str = None, timeout: int = 10) -> 'Custom_WebDriver':
        """
        根据条件查找元素
        
        Args:
            xpath: 元素XPath
            condition_type: 条件类型 
                - 'value_null': 值为空
                - 'value_not_null': 值不为空
                - 'value_equals': 值等于
                - 'value_contains': 值包含
                - 'text_null': 文本为空
                - 'text_not_null': 文本不为空
                - 'text_equals': 文本等于
                - 'text_contains': 文本包含
                - 'enabled': 元素可用
                - 'disabled': 元素不可用
                - 'visible': 元素可见
            expected_value: 期望值（用于equals和contains条件）
            timeout: 超时时间
        """
        start_time = datetime.datetime.now()
        
        while datetime.datetime.now() - start_time < datetime.timedelta(seconds=timeout):
            try:
                element = self.driver.find_element(By.XPATH, xpath)
                
                # 无条件查找，找到即返回
                if not condition_type:
                    self._element = element
                    return self
                
                # 根据条件验证
                if self._check_element_condition(element, condition_type, expected_value):
                    self._element = element
                    return self
                    
            except NoSuchElementException:
                pass
            
            time.sleep(0.5)
        
        print(f"提示: 未找到满足条件的元素: {xpath}")
        return self

    def _check_element_condition(self, element: WebElement, condition_type: str, expected_value: str) -> bool:
        """检查元素条件"""
        try:
            if condition_type == 'value_null':
                return element.get_attribute("value") == ""
            elif condition_type == 'value_not_null':
                return element.get_attribute("value") != ""
            elif condition_type == 'value_equals':
                return element.get_attribute("value") == expected_value
            elif condition_type == 'value_contains':
                return expected_value in element.get_attribute("value")
                
            elif condition_type == 'text_null':
                return element.text == ""
            elif condition_type == 'text_not_null':
                return element.text != ""
            elif condition_type == 'text_equals':
                return element.text == expected_value
            elif condition_type == 'text_contains':
                return expected_value in element.text
                
            elif condition_type == 'enabled':
                return element.is_enabled()
            elif condition_type == 'disabled':
                return not element.is_enabled()
            elif condition_type == 'visible':
                return element.is_displayed()
                
            return False
        except Exception:
            return False



    def upload_file(self, file_path: str) -> 'Custom_WebDriver':
        """
        向当前元素上传文件
        """
        if hasattr(self, '_element') and self._element:
            try:
                if os.path.exists(file_path):
                    self._element.send_keys(file_path)
                    print(f"文件上传成功: {file_path}")
                else:
                    print(f"文件不存在: {file_path}")
            except Exception as e:
                print(f"文件上传失败: {e}")
        else:
            print("提示: 未找到上传元素")
        
        return self

    def 上传文件夹(self, directory_path: str):
        # 1.获取图片列表
        picture_name_list = list()
        for item in pathlib.Path(directory_path).rglob('*'):
            if item.is_file():
                picture_image_path = str(item)
                picture_name_list.append(picture_image_path)
        
        # 2.将图片转换为 Selenium 可以使用的格式
        picture_list_str = ""
        for index, picture_name in enumerate(picture_name_list):
            picture_path = str(pathlib.Path(picture_name).absolute().resolve())
            if index >= 5 : continue
            if index > 0:  # 不是第一个元素时才添加换行符
                picture_list_str += "\n"
            picture_list_str += picture_path
        
        return self.upload_file(picture_list_str)
        
    def 上传文件列表(self,picture_name_list:list):
        # 1.将图片列表转换为 Selenium 可以使用的格式
        picture_list_str = ""
        for index, picture_name in enumerate(picture_name_list):
            picture_path = str(pathlib.Path(picture_name).absolute().resolve())
            if index >= 5 : continue
            if index > 0:  # 不是第一个元素时才添加换行符
                picture_list_str += "\n"
            picture_list_str += picture_path
        
        # 2.上传文件
        return self.upload_file(picture_list_str)
        
        

    def upload_file_by_directory(self, directory_path: str) -> 'Custom_WebDriver':
        """
        上传指定目录下的所有文件
        """
        if hasattr(self, '_element') and self._element:
            try:
                files_to_upload = []
                for root, dirs, files in os.walk(directory_path):
                    for file in files:
                        file_path = os.path.join(root, file)
            except:
                print("提示: 指定目录不存在")
        
        # 1.将图片列表转换为 Selenium 可以使用的格式
        picture_list_str = ""
        for index, picture_name in enumerate(files_to_upload):
            picture_path = str(pathlib.Path(picture_name).absolute().resolve())
            if index >= 5 : continue
            if index > 0:  # 不是第一个元素时才添加换行符
                picture_list_str += "\n"
            picture_list_str += picture_path

        # 2.将图片列表传递给上传元素
        self._element.send_keys(picture_list_str)
        return self
    

    def Script_Move_Click(self) -> 'Custom_WebDriver':
        """将当前选中的元素滚动到视图中并点击。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.execute_script("arguments[0].scrollIntoView();", self._element)
        self.driver.execute_script("arguments[0].click();", self._element)
        return self

    def Safe_Click(self) -> 'Custom_WebDriver':
        _element : WebElement = self._element
        _scrollElement : WebElement = _element
        try:
            self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});",_scrollElement)
            try:
                _element.click()
            except (ElementClickInterceptedException, ElementNotInteractableException):
                self.driver.execute_script("arguments[0].click();", _element)
        except:
            return None
        return self


    def Clean_Input(self, attribute: str = "value") -> 'Custom_WebDriver':
        _element : WebElement = self._element
        _scrollElement : WebElement = _element
        _startTime = datetime.datetime.now()
        _seconds = 5
        _elementAttribute = attribute
        try:
            self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});",_scrollElement)
            try:
                _element.click()
            except (ElementClickInterceptedException, ElementNotInteractableException):
                self.driver.execute_script("arguments[0].click();", _element)
        except:
            pass
        
        if _element.get_attribute(_elementAttribute) != "":
            while _startTime + datetime.timedelta(seconds=_seconds) > datetime.datetime.now():
                _element.send_keys(Keys.BACKSPACE*20)
                if _element.get_attribute(_elementAttribute) == "":
                    break
        return self

    def Safe_SendKeys(self, value, attribute: str = "value") -> 'Custom_WebDriver':
        _element : WebElement = self._element
        _scrollElement : WebElement = _element
        _startTime = datetime.datetime.now()
        _seconds = 5
        _elementAttribute = attribute
        _elementAttribute_value = value
        try:
            self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});",_scrollElement)
            try:
                _element.click()
            except (ElementClickInterceptedException, ElementNotInteractableException):
                self.driver.execute_script("arguments[0].click();", _element)
        except:
            pass
        
        if _element.get_attribute(_elementAttribute) != _elementAttribute_value:
            while _startTime + datetime.timedelta(seconds=_seconds) > datetime.datetime.now():
                _element.send_keys(Keys.BACKSPACE*20)
                if _element.get_attribute(_elementAttribute) == "":
                    break
            _element.send_keys(_elementAttribute_value)
        return self

    def Delay(self, times: float) -> 'Custom_WebDriver':
        """暂停执行指定的时间。

        Args:
            times (float): 暂停的时间（秒）。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        time.sleep(times)
        return self

    def Script_Move_LeftTop(self, Offset_X: int = 0, Offset_Y: int = 0) -> 'Custom_WebDriver':
        """将页面滚动到当前选中元素的左上角偏移指定位置。

        Args:
            Offset_X (int): 水平偏移量。
            Offset_Y (int): 垂直偏移量。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        Element_Local_X = self._element.location['x']
        Element_Local_Y = self._element.location['y']
        self.driver.execute_script(f"window.scrollTo({Element_Local_X - Offset_X},{Element_Local_Y - Offset_Y});")
        return self

    def Script_MoveCenter(self) -> 'Custom_WebDriver':
        """将页面滚动到当前选中元素的中心位置。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});", self._element)
        return self

    def Hover(self) -> 'Custom_WebDriver':
        """将鼠标悬停在当前选中的元素上。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        ActionChains(driver=driver).move_to_element(self._element).perform()
        return self

    def Script_Hover(self, duration: float = 0.5, offset_x: int = 0, offset_y: int = 0) -> 'Custom_WebDriver':
        """
        增强版 JavaScript 悬停
        
        Args:
            driver: WebDriver 实例
            element: 要悬停的元素
            duration: 悬停持续时间（秒）
            offset_x: X 轴偏移量
            offset_y: Y 轴偏移量
        """
        js_script = """
        // 创建鼠标悬停事件
        function simulateHover(element, duration, offsetX, offsetY) {
            // 计算元素位置（带偏移）
            const rect = element.getBoundingClientRect();
            const centerX = rect.left + rect.width / 2 + offsetX;
            const centerY = rect.top + rect.height / 2 + offsetY;
            
            // 创建鼠标移动事件
            const mouseMoveEvent = new MouseEvent('mousemove', {
                view: window,
                bubbles: true,
                cancelable: true,
                clientX: centerX,
                clientY: centerY
            });
            
            // 创建鼠标悬停事件
            const mouseOverEvent = new MouseEvent('mouseover', {
                view: window,
                bubbles: true,
                cancelable: true,
                clientX: centerX,
                clientY: centerY
            });
            
            // 创建鼠标进入事件
            const mouseEnterEvent = new MouseEvent('mouseenter', {
                view: window,
                bubbles: true,
                cancelable: true,
                clientX: centerX,
                clientY: centerY
            });
            
            // 分派事件
            document.dispatchEvent(mouseMoveEvent);
            element.dispatchEvent(mouseOverEvent);
            element.dispatchEvent(mouseEnterEvent);
            
            // 如果需要持续悬停
            if (duration > 0) {
                return new Promise(resolve => {
                    setTimeout(resolve, duration * 1000);
                });
            }
        }
        
        // 执行悬停
        const promise = simulateHover(arguments[0], arguments[1], arguments[2], arguments[3]);
        
        // 如果返回了Promise，等待它完成
        if (promise) {
            const done = arguments[arguments.length - 1];
            promise.then(done);
        }
        """
        
        # 执行脚本并等待完成
        self.driver.execute_async_script(js_script, self._element, duration, offset_x, offset_y)
        return self

    def Capture(self) -> 'Custom_WebDriver':
        """截取当前页面的截图。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        screenshot_base64 = self.driver.get_screenshot_as_base64()
        image_data = base64.b64decode(screenshot_base64)
        image_stream = io.BytesIO(image_data)
        return self

    def Set_implicitly_wait(self, seconds: float = 5) -> 'Custom_WebDriver':
        """设置隐式等待时间。

        Args:
            seconds (float): 隐式等待的时间（秒）。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self.driver.implicitly_wait(seconds)
        return self

    def Set_Current_Element(self, _element: WebElement) -> 'Custom_WebDriver':
        """设置当前操作的元素。

        Args:
            _element (WebElement): 要设置的 WebElement 实例。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        self._element = _element
        return self




    def 验证元素是否存在(self, xpathvalue: str,wait_time: int = 2):
        
        # 检测验证元素是否存在
        _startTime = datetime.datetime.now()
        _seconds = wait_time
        _elementDetectResult : bool = False
        _elementXpathValue = xpathvalue
        while _startTime + datetime.timedelta(seconds=_seconds) > datetime.datetime.now():
            try:
                _element = driver.find_element(by=By.XPATH,value=_elementXpathValue)
                _elementDetectResult = True
            except NoSuchElementException as e:
                _elementDetectResult = False
                continue
            
        # 使用三元表达式返回结果
        return self if _elementDetectResult else None



    def prompt(self, times: float=0.2) -> 'Custom_WebDriver':
        """为当前选中的元素添加红色边框以突出显示。

        Args:
            times (float): 暂停的时间（秒）。

        Returns:
            类_浏览器驱动类: 返回当前实例对象。
        """
        src_style_value = self._element.get_attribute("style")
        new_style_value = f"{src_style_value}; border: 5px solid red;" if src_style_value else "border: 5px solid red;"
        self.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", self._element, new_style_value)
        time.sleep(times)
        self.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", self._element, src_style_value)
        return self

    def find_uploadinput_by_label(self, label: str, max_depth: int = 5) -> 'Custom_WebDriver':
        """通过标签文本查找关联的输入框元素"""
        anchors = self.driver.find_elements(by=By.XPATH, value=f"//*[(self::span or self::label or self::div) and text()='{label}']")
        
        if len(anchors) != 1:
            print(f"找到 {len(anchors)} 个匹配标签，需要唯一标签")
            return self
            
        current_element = anchors[0]
        xpath_pattern = "./parent::*//input[@type='file']"
        
        for _ in range(max_depth):
            try:
                upload_element = current_element.find_element(by=By.XPATH, value=xpath_pattern)
                self._element = upload_element
                return self
            except NoSuchElementException:
                current_element = current_element.find_element(by=By.XPATH, value="./parent::*")
        
        print(f"达到最大查找深度，未找到输入框")
        return self

    def find_upload_input_by_text(self, text: str, max_depth: int = 5) -> 'Custom_WebDriver':
        """
        通过附近文本查找文件上传输入框
        这是 find_uploadinput_by_label 的增强版
        """
        return self.by_near_text_find_element(
            text=text,
            element_tag="input",
            element_type="file",
            text_tags=['span', 'label', 'div', 'p', 'button', 'a'],
            max_depth=max_depth
        )

if __name__ == "__main__":
    print("提示 : 模块测试")
    driver = Custom_WebDriver()
    driver.Goto_CurrentActivateUrl()
    
    